﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;

namespace TotalFreedomKO
{
    public enum KoValueUpdate
    { 
        FocusChange = 0,
        KeyUp = 1,
        KeyPresss = 2,
        AfterKeyDown = 4,
    }

    public static partial class KoAttributeHelpers
    {
        #region ko attributes

        #region ko template attributes

        public static IKoAttributeHtmlString<TModel> KoAttrTemplate<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string templateName)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrTemplate(null, expression, templateName, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrTemplate<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, string templateName, ViewDataDictionary<TModel> viewData = null)
        {
            if(String.IsNullOrWhiteSpace(templateName))
                throw(new ArgumentException("Template name must be specified"));

            string s = "template: { name: '" + HttpUtility.HtmlAttributeEncode(Regex.Replace(templateName, "[^a-zA-Z0-9_]", "")) + "'";

            if (expression != null)
            {
                PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);
                if (data != null && !String.IsNullOrEmpty(data.PropertyName))
                    s += ", data: " + HttpUtility.HtmlAttributeEncode( data.PropertyName );
            }

            s += "}";

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "template", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrTemplateForeach<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string templateName) where TProperty : IEnumerable
        {
            if (htmlHelper == null)
                return null;

            return KoAttrTemplateForeach(null, expression, templateName, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrTemplateForeach<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, string templateName, ViewDataDictionary<TModel> viewData = null) where TProperty : IEnumerable
        {
            if (String.IsNullOrWhiteSpace(templateName))
                throw (new ArgumentException("Template name must be specified"));

            string s = "template: { name: '" + HttpUtility.HtmlAttributeEncode(Regex.Replace(templateName, "[^a-zA-Z0-9_]", "")) + "'";

            if (expression != null)
            {
                PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);
                if (data != null && !String.IsNullOrEmpty(data.PropertyName))
                    s += ", foreach: " + HttpUtility.HtmlAttributeEncode(data.PropertyName);
            }

            s += "}";

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "template", s, viewData);
        }
        #endregion

        #region ko controlling text and appearance

        public static IKoAttributeHtmlString<TModel> KoAttrVisible<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrVisible(null, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrVisible<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            PropertyData data = KoHelperExtensions.GetPropertyData(expression);

            string s = "visible: " + HttpUtility.HtmlAttributeEncode(data.Expression);

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "visible", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrText<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrText(null, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrText<TModel, TProperty>(this IKoHelper<TModel> koHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (koHelper == null)
                return null;            

            return KoAttrText(null, expression, null);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrText<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            PropertyData data = KoHelperExtensions.GetPropertyData(expression);

            string s = "text: " + HttpUtility.HtmlAttributeEncode(data.Expression);

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "text", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrHtml<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrHtml(null, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrHtml<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            PropertyData data = KoHelperExtensions.GetPropertyData(expression);

            string s = "html: " + HttpUtility.HtmlAttributeEncode(data.Expression);

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "html", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrCSS<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string cssClassName, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrCSS(null, cssClassName, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrCSS<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, string cssClassName, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            if(String.IsNullOrWhiteSpace(cssClassName))
                throw(new ArgumentException("Must specify CSS class name"));

            PropertyData data = KoHelperExtensions.GetPropertyData(expression);

            // because CSS allows multiple values, we build only the this specific part
            string s = "'" + HttpUtility.HtmlAttributeEncode(cssClassName) + "': " + HttpUtility.HtmlAttributeEncode(data.Expression);

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "css", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrStyle<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string styleAttr, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrStyle(null, styleAttr, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrStyle<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, string styleAttr, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            if (String.IsNullOrWhiteSpace(styleAttr))
                throw (new ArgumentException("Must specify CSS style attribute name"));

            PropertyData data = KoHelperExtensions.GetPropertyData(expression);

            // because "Style" allows multiple values, we build only the this specific part
            string s = "'" + HttpUtility.HtmlAttributeEncode(styleAttr) + "': " + HttpUtility.HtmlAttributeEncode(data.Expression);

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "style", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttr<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, string attributeName, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttr(null, attributeName, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttr<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, string attributeName, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            if (String.IsNullOrWhiteSpace(attributeName))
                throw (new ArgumentException("Must specify attribute name"));

            PropertyData data = KoHelperExtensions.GetPropertyData(expression);

            // because knockout "Attr" allows multiple values, we build only the this specific part
            string s = "'" + HttpUtility.HtmlAttributeEncode(attributeName) + "': " + HttpUtility.HtmlAttributeEncode(data.Expression);

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "attr", s, viewData);
        }

        #endregion

        #region control flow

        public static KoAttributeHtmlString2<TModel, TProperty> KoAttrForeach<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, IEnumerable<TProperty>>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrForeach(null, expression, htmlHelper.ViewData);
        }

        public static KoAttributeHtmlString3<TModel, TProperty> KoAttrForeach<TModel, TProperty>(this IKoHelper<TModel> koHelper, Expression<Func<TModel, IEnumerable<TProperty>>> expression)
        {
            if (koHelper == null)
                return null;

            string s = "";

            if (expression != null)
            {
                PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);
                if (data != null && !String.IsNullOrEmpty(data.PropertyName))
                    s += "foreach: " + HttpUtility.HtmlAttributeEncode(data.PropertyName);
            }

            return new KoAttributeHtmlString3<TModel, TProperty>(koHelper.HtmlHelper, null, "foreach", s, null);
        }

        public static KoAttributeHtmlString2<TModel, TProperty> KoAttrForeach<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, IEnumerable<TProperty>>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            string s = "";

            if (expression != null)
            {
                PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);
                if (data != null && !String.IsNullOrEmpty(data.PropertyName))
                    s += "foreach: " + HttpUtility.HtmlAttributeEncode(data.PropertyName);
            }

            return new KoAttributeHtmlString2<TModel, TProperty>(koAttribute, "foreach", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrIf<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrIf(null, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrIf<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            string s = "";

            if (expression != null)
            {
                PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);
                if (data != null && !String.IsNullOrEmpty(data.Expression))
                    s += "if: " + HttpUtility.HtmlAttributeEncode(data.Expression);
            }

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "if", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrIfnot<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrIfnot(null, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrIfnot<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            string s = "";

            if (expression != null)
            {
                PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);
                if (data != null && !String.IsNullOrEmpty(data.Expression))
                    s += "ifnot: " + HttpUtility.HtmlAttributeEncode(data.Expression);
            }

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "ifnot", s, viewData);
        }

        public static KoAttributeHtmlString3<TModel, TProperty> KoAttrWith<TModel, TProperty>(this IKoHelper<TModel> koHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (koHelper == null)
                return null;

            string s = "";

            if (expression != null)
            {
                PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);
                if (data != null && !String.IsNullOrEmpty(data.Expression))
                    s += "with: " + HttpUtility.HtmlAttributeEncode(data.Expression);
            }

            return new KoAttributeHtmlString3<TModel, TProperty>(koHelper.HtmlHelper, null, "with", s, null);
        }

        public static KoAttributeHtmlString2<TModel, TProperty> KoAttrWith<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrWith(null, expression, htmlHelper.ViewData);
        }

        public static KoAttributeHtmlString2<TModel, TProperty> KoAttrWith<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            string s = "";

            if (expression != null)
            {
                PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);
                if (data != null && !String.IsNullOrEmpty(data.Expression))
                    s += "with: " + HttpUtility.HtmlAttributeEncode(data.Expression);
            }

            return new KoAttributeHtmlString2<TModel, TProperty>(koAttribute, "with", s, viewData);
        }
        
        #endregion

        #region form fields

        public static IKoAttributeHtmlString<TModel> KoAttrValue<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, KoValueUpdate valueUpdate = KoValueUpdate.FocusChange)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrValue(null, expression, valueUpdate, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrValue<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, KoValueUpdate valueUpdate = KoValueUpdate.FocusChange, ViewDataDictionary<TModel> viewData = null)
        {
            PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);

            string s = "value: " + data.PropertyName;

            if (valueUpdate > 0)
                s += ", valueUpdate: '" + valueUpdate.ToString().ToLower() + "'";

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "value", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrDisable<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrDisable(null, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrDisable<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            PropertyData data = KoHelperExtensions.GetPropertyData(expression);

            string s = "disable: " + data.Expression;

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "disable", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrEnable<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrEnable(null, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrEnable<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            PropertyData data = KoHelperExtensions.GetPropertyData(expression);

            string s = "enable: " + data.Expression;

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "enable", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrChecked<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrChecked(null, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrChecked<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, TProperty>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);

            string s = "checked: " + data.PropertyName;

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "checked", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrOptions<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, IEnumerable<TProperty>>> expression, Expression<Func<TProperty, object>> textExpression = null, Expression<Func<TProperty, object>> valueExpression = null, string caption = null)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrOptions(null, expression, htmlHelper.ViewData, textExpression, valueExpression, caption);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrOptions<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, IEnumerable<TProperty>>> expression, ViewDataDictionary<TModel> viewData = null, Expression<Func<TProperty, object>> textExpression = null, Expression<Func<TProperty, object>> valueExpression = null, string caption = null)
        {
            PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);

            string s = "options: " + data.PropertyName;

            if (textExpression != null)
            {
                PropertyData data2 = KoHelperExtensions.GetPropertyData(textExpression, propertyNamePrefix: "item.");
                if (data2.HasExpression)
                    s += ",  optionsText: function(item) { return " + HttpUtility.HtmlAttributeEncode(data2.Expression) + "}";
                else
                    s += ",  optionsText: '" + HttpUtility.HtmlAttributeEncode(data2.Expression) + "'";
            }

            if (valueExpression != null)
            {
                PropertyData data3 = KoHelperExtensions.GetPropertyData(valueExpression, propertyNamePrefix: "item.");
                if (data3.HasExpression)
                    s += ",  optionsText: function(item) { return " + HttpUtility.HtmlAttributeEncode(data3.Expression) + "}";
                else
                    s += ",  optionsText: '" + HttpUtility.HtmlAttributeEncode(data3.Expression) + "'";
            }

            if (!String.IsNullOrWhiteSpace(caption))
                s += ",  optionsCaption: '" + HttpUtility.HtmlAttributeEncode(caption) + "'";

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "options", s, viewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrSelectedOptions<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, IEnumerable<TProperty>>> expression, Expression<Func<TProperty, object>> textExpression = null, Expression<Func<TProperty, object>> valueExpression = null)
        {
            if (htmlHelper == null)
                return null;

            return KoAttrSelectedOptions(null, expression, htmlHelper.ViewData);
        }

        public static IKoAttributeHtmlString<TModel> KoAttrSelectedOptions<TModel, TProperty>(this IKoAttributeHtmlString<TModel> koAttribute, Expression<Func<TModel, IEnumerable<TProperty>>> expression, ViewDataDictionary<TModel> viewData = null)
        {
            PropertyData data = KoHelperExtensions.GetPropertyData(expression, true);

            string s = "selectedOptions: " + data.PropertyName;

            return new KoAttributeHtmlString<TModel, TProperty>(koAttribute, "selectedOptions", s, viewData);
        } 

        #endregion

        #endregion

        #region helpers

        public static string GetKoAttrHtmlEncodedValue(object v)
        { 
            if(v == null)
                return "";

            if (v is int)
            {
                return "" + (int)v;
            }
            else if (v is bool)
            {
                bool v2 = (bool)v;
                return v2 ? "true" : "false";
            }
            else
                return "'" + HttpUtility.HtmlEncode(v.ToString()) + "'";
        }

        #endregion

    }
}